package com.zzvcom.doc.html;

import net.sf.saxon.TransformerFactoryImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmlresolver.Resolver;
import org.xmlresolver.ResolverFeature;
import org.xmlresolver.XMLResolverConfiguration;

import javax.xml.transform.*;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * StreamSource 必须带 systemId：new StreamSource(xsltUrl.openStream(), xsltUrl.toString()) 让 Saxon 明确 XSLT 文件的 “基准路径”，这样 <xsl:import> 的相对路径才能从该基准正确解析。
 * catalog.xml 映射精准：rewritePrefix 正确指向本地 mml2tex/ 和 xslt-util/ 目录，让网络 URI 能被替换为存在的本地 XSLT 文件
 */
public class Mml2TexConverter {
    // 配置路径（确保和 resources 目录结构一致）
    private static final String MML2TEX_XSL = "mml2tex/xsl/invoke-mml2tex.xsl";
    private static final String CATALOG_PATH = "xmlcatalog/catalog.xml";
    private static final Logger LOG = LoggerFactory.getLogger(Mml2TexConverter.class);
    // 缓存 XSLT URL 和 Resolver 以提高性能并确保线程安全
    private static URL xsltUrl;
    private static Resolver resolver;

    // 使用 ThreadLocal 缓存 TransformerFactory 提高多线程性能
    private static final ThreadLocal<TransformerFactory> TRANSFORMER_FACTORY_CACHE =
        ThreadLocal.withInitial(() -> {
            TransformerFactory factory = new TransformerFactoryImpl();
            factory.setURIResolver(resolver);
            factory.setAttribute("http://saxon.sf.net/feature/allow-external-functions", true);
            return factory;
        });

    static {
        try {
            initResources();
        } catch (Exception e) {
            LOG.error("Failed to initialize resources", e);
            throw new ExceptionInInitializerError(e);
        }
    }

    // 初始化共享资源
    private static void initResources() throws Exception {
        // 1. 检查 catalog.xml 是否存在（抛明确异常）
        URL catalogUrl = Mml2TexConverter.class.getClassLoader().getResource(CATALOG_PATH);
        if (catalogUrl == null) {
            throw new FileNotFoundException(
                    "❌ catalog.xml 未找到！\n" +
                            "请确保文件路径：src/main/resources/" + CATALOG_PATH
            );
        }
        URI catalogUri = catalogUrl.toURI();
        LOG.info("✅ 加载 Catalog：{}", catalogUri.getPath());

        // 2. 配置 Resolver（加类加载器+Catalog列表）
        XMLResolverConfiguration config = new XMLResolverConfiguration();
        // 设置 Catalog 文件（符合 xmlresolver 3.x 规范）
        List<String> catalogs = new ArrayList<>();
        catalogs.add(catalogUri.toString());
        config.setFeature(ResolverFeature.CATALOG_FILES, catalogs);
        resolver = new Resolver(config);

        // 获取 XSLT URL
        xsltUrl = Mml2TexConverter.class.getClassLoader().getResource(MML2TEX_XSL);
        if (xsltUrl == null) {
            throw new FileNotFoundException(
                    "❌ XSLT file not found: " + MML2TEX_XSL
            );
        }
    }

    // 核心转换方法（MathML → LaTeX）- 接受文件路径
    public static String convertMathML(String inputXmlPath) throws Exception {
        // 1. 加载输入文件
        Source inputSource;
        File localFile = new File(inputXmlPath);
        if (localFile.exists()) {
            inputSource = new StreamSource(new FileInputStream(localFile), localFile.toURI().toString());
            LOG.info("✅ 从本地路径加载输入文件：{}", localFile.getAbsolutePath());
        } else {
            InputStream inputStream = Mml2TexConverter.class.getClassLoader().getResourceAsStream(inputXmlPath);
            if (inputStream == null) {
                throw new FileNotFoundException(
                        "❌ formula.xml 未找到！\n" +
                                "请将文件放在以下任一位置：\n" +
                                "1. 项目根目录（本地路径）\n" +
                                "2. src/main/resources/（classpath 路径）"
                );
            }
            inputSource = new StreamSource(inputStream);
            LOG.info("✅ 从 classpath 加载输入文件：{}", inputXmlPath);
        }

        // 2. 执行转换
        return performConversion(inputSource);
    }

    // 新增方法：接受字符串输入的转换方法（线程安全）
    public static String convertMathMLFromString(String mathML) throws Exception {
        // 创建输入源，直接使用 StringReader 避免字节转换开销
        Source inputSource = new StreamSource(new StringReader(mathML));
        // 执行转换
        return performConversion(inputSource);
    }

    // 批量转换方法，支持并行处理提高多线程性能
    public static List<String> convertMathMLBatch(List<String> mathMLList) throws Exception {
        return mathMLList.parallelStream()
            .map(mathML -> {
                try {
                    return convertMathMLFromString(mathML);
                } catch (Exception e) {
                    LOG.error("Error converting MathML: {}", mathML, e);
                    return ""; // 返回空字符串表示转换失败
                }
            })
            .collect(Collectors.toList());
    }

    // 执行实际的转换操作（线程安全）
    private static String performConversion(Source inputSource) throws Exception {
        // 每次创建新的 Transformer 实例以确保线程安全
        Source xsltSource = new StreamSource(xsltUrl.openStream(), xsltUrl.toString());

        // 使用 ThreadLocal 缓存的 TransformerFactory 提高性能
        TransformerFactory factory = TRANSFORMER_FACTORY_CACHE.get();
        Transformer transformer = factory.newTransformer(xsltSource);

        // 执行转换
        StringWriter resultWriter = new StringWriter();
        transformer.transform(inputSource, new StreamResult(resultWriter));
        LOG.debug("✅ 转换完成！");

        return extractMml2Tex(resultWriter.toString());
    }

    /**
     * 从XML处理指令字符串中提取mml2tex内容并转换为LaTeX显示模式
     * @param xmlProcessingInstruction 包含mml2tex处理指令的XML字符串
     * @return 格式化后的LaTeX公式字符串
     */
    private static String extractMml2Tex(String xmlProcessingInstruction) {
        // 检查输入是否包含mml2tex处理指令
        if (xmlProcessingInstruction == null || !xmlProcessingInstruction.contains("mml2tex")) {
            return xmlProcessingInstruction;
        }

        // 使用更高效的方式提取处理指令中的内容
        int startIndex = xmlProcessingInstruction.indexOf("mml2tex") + 8; // "mml2tex ".length() = 8
        int endIndex = xmlProcessingInstruction.lastIndexOf("?>");

        if (startIndex <= 7 || endIndex <= startIndex) {
            return xmlProcessingInstruction;
        }

        // 提取内容并去除可能的空格
        return xmlProcessingInstruction.substring(startIndex, endIndex).trim();
    }

    // 测试入口
    public static void main(String[] args) {
        try {
            String latexResult = convertMathML("formula.xml");
            System.out.println("\n======================================");
            System.out.println("通过文件路径转换后的 LaTeX 结果：");
            System.out.println(latexResult);
            System.out.println("======================================\n");

            // 测试字符串输入
            String mathML =      "\n" +
                    "    <mi>x</mi>\n" +
                    "    <mo>=</mo>\n" +
                    "    <mfrac>\n" +
                    "        <mrow>\n" +
                    "            <mo>-</mo>\n" +
                    "            <mi>b</mi>\n" +
                    "            <mo>±</mo>\n" +
                    "            <msqrt>\n" +
                    "                <mrow>\n" +
                    "                    <msup><mi>b</mi><mn>2</mn></msup>\n" +
                    "                    <mo>-</mo>\n" +
                    "                    <mn>4</mn>\n" +
                    "                    <mi>a</mi>\n" +
                    "                    <mi>c</mi>\n" +
                    "                </mrow>\n" +
                    "            </msqrt>\n" +
                    "        </mrow>\n" +
                    "        <mrow>\n" +
                    "            <mn>2</mn>\n" +
                    "            <mi>a</mi>\n" +
                    "        </mrow>\n" +
                    "    </mfrac>\n" ;
            mathML = mathML.replace("\n", "").replace(" ", "");
          mathML= "<math xmlns=\"http://www.w3.org/1998/Math/MathML\">" + mathML + "</math>";
            String latexResult2 = convertMathMLFromString(mathML);
            System.out.println("\n======================================");
            System.out.println("通过字符串转换后的 LaTeX 结果：");
            System.out.println(latexResult2);
            System.out.println("======================================\n");
        } catch (Exception e) {
            LOG.error("❌ 转换失败：", e);
            e.printStackTrace();
        }
    }
}
